package com.yuno.mock.controller;

import com.yuno.mock.dto.ApiResponse;
import com.yuno.mock.dto.FileUploadResponse;
import com.yuno.mock.dto.ProgressResponse;
import com.yuno.mock.service.MinioService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * MinIO文件管理控制器
 *
 * @author xrm
 * @since 2025-11-06
 */
@Slf4j
@RestController
@RequestMapping("/api/minio")
@RequiredArgsConstructor
@Tag(name = "MinIO文件管理", description = "MinIO文件上传、下载、管理相关接口")
public class MinioController {

    private final MinioService minioService;

    /**
     * 初始化MinIO存储桶
     */
    @PostMapping("/init")
    @Operation(summary = "初始化MinIO存储桶", description = "创建并初始化MinIO存储桶")
    public ResponseEntity<ApiResponse<String>> initBucket() {
        try {
            minioService.initBucket();
            return ResponseEntity.ok(ApiResponse.success("存储桶初始化成功"));
        } catch (Exception e) {
            log.error("初始化存储桶失败", e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("存储桶初始化失败: " + e.getMessage()));
        }
    }

    /**
     * 上传文件（同步）
     */
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @Operation(summary = "上传文件", description = "同步上传文件到MinIO")
    public ResponseEntity<ApiResponse<FileUploadResponse>> uploadFile(
            @Parameter(description = "上传的文件", required = true)
            @RequestParam("file") MultipartFile file,

            @Parameter(description = "自定义文件名")
            @RequestParam(value = "fileName", required = false) String fileName,

            @Parameter(description = "文件描述")
            @RequestParam(value = "description", required = false) String description) {

        try {
            // 验证文件
            if (file.isEmpty()) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.badRequest("上传文件不能为空"));
            }

            // 使用自定义文件名或自动生成
            String finalFileName = (fileName != null && !fileName.trim().isEmpty())
                    ? fileName : null;

            // 上传文件
            String fileUrl = minioService.uploadFile(file, finalFileName);

            // 构建响应
            FileUploadResponse response = FileUploadResponse.builder()
                    .fileName(finalFileName != null ? finalFileName : file.getOriginalFilename())
                    .fileUrl(fileUrl)
                    .fileSize(file.getSize())
                    .contentType(file.getContentType())
                    .status("SUCCESS")
                    .build();

            return ResponseEntity.ok(ApiResponse.success(response, "文件上传成功"));

        } catch (Exception e) {
            log.error("文件上传失败", e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("文件上传失败: " + e.getMessage()));
        }
    }

    /**
     * 上传文件（异步）
     */
    @PostMapping(value = "/upload/async", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @Operation(summary = "异步上传文件", description = "异步上传文件到MinIO，返回任务ID")
    public ResponseEntity<ApiResponse<FileUploadResponse>> uploadFileAsync(
            @Parameter(description = "上传的文件", required = true)
            @RequestParam("file") MultipartFile file,

            @Parameter(description = "自定义文件名")
            @RequestParam(value = "fileName", required = false) String fileName) {

        try {
            // 验证文件
            if (file.isEmpty()) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.badRequest("上传文件不能为空"));
            }

            // 生成任务ID
            String taskId = UUID.randomUUID().toString();

            // 异步上传文件
            CompletableFuture<String> uploadFuture = minioService.uploadFileAsync(file);

            // 构建响应
            FileUploadResponse response = FileUploadResponse.builder()
                    .fileName(fileName != null ? fileName : file.getOriginalFilename())
                    .fileSize(file.getSize())
                    .contentType(file.getContentType())
                    .taskId(taskId)
                    .status("IN_PROGRESS")
                    .build();

            // 异步处理上传完成后的操作
            uploadFuture.whenComplete((fileUrl, throwable) -> {
                if (throwable != null) {
                    log.error("异步文件上传失败，任务ID: {}", taskId, throwable);
                } else {
                    log.info("异步文件上传成功，任务ID: {}, 文件URL: {}", taskId, fileUrl);
                }
            });

            return ResponseEntity.ok(ApiResponse.success(response, "异步文件上传已开始"));

        } catch (Exception e) {
            log.error("启动异步文件上传失败", e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("启动异步文件上传失败: " + e.getMessage()));
        }
    }

    /**
     * 下载文件
     */
    @GetMapping("/download/{fileName:.+}")
    @Operation(summary = "下载文件", description = "从MinIO下载文件")
    public ResponseEntity<InputStreamResource> downloadFile(
            @Parameter(description = "文件名", required = true)
            @PathVariable String fileName) {

        try {
            // URL解码文件名
            String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8.name());

            // 检查文件是否存在
            if (!minioService.fileExists(decodedFileName)) {
                return ResponseEntity.notFound().build();
            }

            // 下载文件
            InputStream inputStream = minioService.downloadFile(decodedFileName);

            // 获取文件扩展名确定Content-Type
            String contentType = "application/octet-stream";
            if (decodedFileName.contains(".")) {
                String extension = decodedFileName.substring(decodedFileName.lastIndexOf(".") + 1).toLowerCase();
                switch (extension) {
                    case "pdf":
                        contentType = "application/pdf";
                        break;
                    case "jpg":
                    case "jpeg":
                        contentType = "image/jpeg";
                        break;
                    case "png":
                        contentType = "image/png";
                        break;
                    case "gif":
                        contentType = "image/gif";
                        break;
                    case "txt":
                        contentType = "text/plain";
                        break;
                    case "json":
                        contentType = "application/json";
                        break;
                    case "xml":
                        contentType = "application/xml";
                        break;
                    case "zip":
                        contentType = "application/zip";
                        break;
                }
            }

            // 构建响应头
            String encodedFileName = URLEncoder.encode(decodedFileName, StandardCharsets.UTF_8.name());

            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(contentType))
                    .header(HttpHeaders.CONTENT_DISPOSITION,
                            "attachment; filename=\"" + encodedFileName + "\"")
                    .body(new InputStreamResource(inputStream));

        } catch (Exception e) {
            log.error("文件下载失败: {}", fileName, e);
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * 异步下载文件
     */
    @PostMapping("/download/async")
    @Operation(summary = "异步下载文件", description = "异步下载文件，返回任务ID")
    public ResponseEntity<ApiResponse<Map<String, String>>> downloadFileAsync(
            @Parameter(description = "文件名", required = true)
            @RequestParam("fileName") String fileName) {

        try {
            // 检查文件是否存在
            if (!minioService.fileExists(fileName)) {
                return ResponseEntity.notFound().build();
            }

            // 生成任务ID
            String taskId = UUID.randomUUID().toString();

            // 异步下载文件
            CompletableFuture<byte[]> downloadFuture = minioService.downloadFileAsync(fileName);

            // 异步处理下载完成
            downloadFuture.whenComplete((fileBytes, throwable) -> {
                if (throwable != null) {
                    log.error("异步文件下载失败，任务ID: {}, 文件名: {}", taskId, fileName, throwable);
                } else {
                    log.info("异步文件下载成功，任务ID: {}, 文件大小: {} bytes", taskId, fileBytes.length);
                    // 这里可以将文件保存到临时位置或通过其他方式通知客户端
                }
            });

            Map<String, String> response = new HashMap<>();
            response.put("taskId", taskId);
            response.put("fileName", fileName);
            response.put("status", "IN_PROGRESS");

            return ResponseEntity.ok(ApiResponse.success(response, "异步文件下载已开始"));

        } catch (Exception e) {
            log.error("启动异步文件下载失败: {}", fileName, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("启动异步文件下载失败: " + e.getMessage()));
        }
    }

    /**
     * 删除文件
     */
    @DeleteMapping("/delete/{fileName:.+}")
    @Operation(summary = "删除文件", description = "从MinIO删除文件")
    public ResponseEntity<ApiResponse<String>> deleteFile(
            @Parameter(description = "文件名", required = true)
            @PathVariable String fileName) {

        try {
            // URL解码文件名
            String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8.name());

            // 检查文件是否存在
            if (!minioService.fileExists(decodedFileName)) {
                return ResponseEntity.notFound().build();
            }

            // 删除文件
            minioService.deleteFile(decodedFileName);

            return ResponseEntity.ok(ApiResponse.success(null, "文件删除成功"));

        } catch (Exception e) {
            log.error("文件删除失败: {}", fileName, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("文件删除失败: " + e.getMessage()));
        }
    }

    /**
     * 获取文件列表
     */
    @GetMapping("/files")
    @Operation(summary = "获取文件列表", description = "获取MinIO存储桶中的所有文件列表")
    public ResponseEntity<ApiResponse<List<Map<String, Object>>>> listFiles() {
        try {
            List<Map<String, Object>> files = minioService.listFiles();
            return ResponseEntity.ok(ApiResponse.success(files, "获取文件列表成功"));
        } catch (Exception e) {
            log.error("获取文件列表失败", e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("获取文件列表失败: " + e.getMessage()));
        }
    }

    /**
     * 获取文件URL
     */
    @GetMapping("/url/{fileName:.+}")
    @Operation(summary = "获取文件URL", description = "获取文件的访问URL")
    public ResponseEntity<ApiResponse<Map<String, String>>> getFileUrl(
            @Parameter(description = "文件名", required = true)
            @PathVariable String fileName) {

        try {
            // URL解码文件名
            String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8.name());

            // 检查文件是否存在
            if (!minioService.fileExists(decodedFileName)) {
                return ResponseEntity.notFound().build();
            }

            // 获取文件URL
            String fileUrl = minioService.getFileUrl(decodedFileName);

            Map<String, String> response = new HashMap<>();
            response.put("fileName", decodedFileName);
            response.put("fileUrl", fileUrl);

            return ResponseEntity.ok(ApiResponse.success(response, "获取文件URL成功"));

        } catch (Exception e) {
            log.error("获取文件URL失败: {}", fileName, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("获取文件URL失败: " + e.getMessage()));
        }
    }

    /**
     * 检查文件是否存在
     */
    @GetMapping("/exists/{fileName:.+}")
    @Operation(summary = "检查文件是否存在", description = "检查指定文件是否存在于MinIO中")
    public ResponseEntity<ApiResponse<Map<String, Object>>> checkFileExists(
            @Parameter(description = "文件名", required = true)
            @PathVariable String fileName) {

        try {
            // URL解码文件名
            String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8.name());

            boolean exists = minioService.fileExists(decodedFileName);

            Map<String, Object> response = new HashMap<>();
            response.put("fileName", decodedFileName);
            response.put("exists", exists);

            return ResponseEntity.ok(ApiResponse.success(response, "检查完成"));

        } catch (Exception e) {
            log.error("检查文件存在性失败: {}", fileName, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("检查文件存在性失败: " + e.getMessage()));
        }
    }

    /**
     * 获取上传进度
     */
    @GetMapping("/progress/upload/{taskId}")
    @Operation(summary = "获取上传进度", description = "获取异步文件上传的进度")
    public ResponseEntity<ApiResponse<ProgressResponse>> getUploadProgress(
            @Parameter(description = "任务ID", required = true)
            @PathVariable String taskId) {

        try {
            Double progress = minioService.getUploadProgress(taskId);
            String status = minioService.getTaskStatus(taskId);

            ProgressResponse response = ProgressResponse.builder()
                    .taskId(taskId)
                    .progress(progress)
                    .status(status)
                    .statusDescription(getStatusDescription(status))
                    .build();

            return ResponseEntity.ok(ApiResponse.success(response, "获取进度成功"));

        } catch (Exception e) {
            log.error("获取上传进度失败: {}", taskId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("获取上传进度失败: " + e.getMessage()));
        }
    }

    /**
     * 获取下载进度
     */
    @GetMapping("/progress/download/{taskId}")
    @Operation(summary = "获取下载进度", description = "获取异步文件下载的进度")
    public ResponseEntity<ApiResponse<ProgressResponse>> getDownloadProgress(
            @Parameter(description = "任务ID", required = true)
            @PathVariable String taskId) {

        try {
            Double progress = minioService.getDownloadProgress(taskId);
            String status = minioService.getTaskStatus(taskId);

            ProgressResponse response = ProgressResponse.builder()
                    .taskId(taskId)
                    .progress(progress)
                    .status(status)
                    .statusDescription(getStatusDescription(status))
                    .build();

            return ResponseEntity.ok(ApiResponse.success(response, "获取进度成功"));

        } catch (Exception e) {
            log.error("获取下载进度失败: {}", taskId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("获取下载进度失败: " + e.getMessage()));
        }
    }

    /**
     * 获取状态描述
     */
    private String getStatusDescription(String status) {
        switch (status) {
            case "PENDING":
                return "等待处理";
            case "UPLOADING":
                return "正在上传";
            case "DOWNLOADING":
                return "正在下载";
            case "COMPLETED":
                return "已完成";
            case "FAILED":
                return "处理失败";
            default:
                return "未知状态";
        }
    }
}